-
Notifications
You must be signed in to change notification settings - Fork 48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding slack integration for posting to channel on applies. Fixes #179 #180
Adding slack integration for posting to channel on applies. Fixes #179 #180
Conversation
server/server.go
Outdated
@@ -61,6 +64,14 @@ func NewServer(config Config) (*Server, error) { | |||
return nil, err | |||
} | |||
githubStatus := &events.GithubStatus{Client: githubClient} | |||
// nil slackClient unless token and channel was specified | |||
var slackClient slack.Client | |||
if config.SlackToken != "" && config.SlackChannel != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only attempt to create a slack client if both token
and channel
are supplied.
If we decide to only use a token
and channel
, I plan to add a warning for when the user supplies ONLY one item. But the schema will probably be changed after discussion in #179 and this'll be irrelevant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general it's dangerous to use nil
for something like this because it's not obvious that my slack client will be nil just because somewhere up the chain depending on the config. On the other hand, you can't initialize a slack client if you don't have the config :D.
I would create a NoopSlackClient that implements the interface but doesn't do anything and use it here if there is no slack config.
server/events/apply_executor.go
Outdated
@@ -106,3 +114,19 @@ func (a *ApplyExecutor) apply(ctx *CommandContext, repoDir string, plan models.P | |||
|
|||
return ProjectResult{ApplySuccess: output} | |||
} | |||
|
|||
func createSlackMessage(ctx *CommandContext, success bool) string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this functionality doesn't belong in the apply executor since it's all about applying, not creating slack messages. Put this in the slack client.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't use package functions like this, instead add it to the struct.
func (a *ApplyExecutor) createSlackMessage
If you don't do this then anyone can call this method from anywhere in this package which is weird, you really only want it called from the object
server/events/apply_executor.go
Outdated
status = ":x:" | ||
} | ||
|
||
return fmt.Sprintf("%s *%s* %s in <%s|%s>.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you add a screenshot so we can see what this looks like?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
server/events/slack/slack_client.go
Outdated
} | ||
|
||
// https://api.slack.com/faq | ||
// 'How do I find a channel's ID if I only have its #name?' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need this. It says you can just use the name: https://api.slack.com/methods/chat.postMessage#channels
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With chat.postMessage
you can use a channel name, but if the channel doesn't exist, it doesn't send.
I was thinking of making sure the channel
from config exists on the creation of the slack client and failing early if it doesn't, rather than failing at chat.postMessage
or failing silently.
So to do that, I looked at https://api.slack.com/methods/channels.info which requires an ID. Alternatively, I can do a chat.postMessage
and see if it succeeds but that would post a message? Or there might be other ways!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of failing when Atlantis server starts if that channel doesn't exist. Failing early is usually better so good call.
server/events/slack/slack_client.go
Outdated
} | ||
} | ||
|
||
return nil, errors.New("channel_not_found") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
errors should be human readable so you could have just had "channel doesn't exist"
server/server.go
Outdated
@@ -61,6 +64,14 @@ func NewServer(config Config) (*Server, error) { | |||
return nil, err | |||
} | |||
githubStatus := &events.GithubStatus{Client: githubClient} | |||
// nil slackClient unless token and channel was specified | |||
var slackClient slack.Client | |||
if config.SlackToken != "" && config.SlackChannel != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general it's dangerous to use nil
for something like this because it's not obvious that my slack client will be nil just because somewhere up the chain depending on the config. On the other hand, you can't initialize a slack client if you don't have the config :D.
I would create a NoopSlackClient that implements the interface but doesn't do anything and use it here if there is no slack config.
This reverts commit 3be7f8c.
Thanks for the awesome review and feedback!
TODO:
|
server/server.go
Outdated
for _, webhook := range config.Webhooks { | ||
hookExecutor, err := hookExecutorBuilder.build(webhook) | ||
if err != nil { | ||
// TODO: check if it was because of uninitialized slack client |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If slack-token
wasn't provided in the config, hookExecutorBuilder
would have a nil
slack.Client
. This will cause an error when building slack
webhooks, then Atlantis can error saying the user needs to provide a slack-token
.
But this doesn't seem too clean, any suggestions? Using a NoopClient
would work, but if we can error early and tell the user what's wrong, I feel like that's the way to go!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my suggestions I have a WebhooksManager
that has a []WebhookSender
. When it gets constructed with no webhooks then it just has an empty array and so calling WebhooksManager.Send()
just does nothing. That's how I handled it.
When it's running through the webhooks in its constructor, it returns an error
if a slack webhook was specified but there was no slack-token
: https://github.com/nicholas-wu-hs/atlantis/pull/1/files?diff=unified#diff-0250d5b43682815402ad30ce1b068cdcR55
server/events/apply_executor.go
Outdated
matched, err := regexp.MatchString(workspaceRegex, ctx.Command.Environment) | ||
if err != nil { | ||
// log the regexp error but let's continue with the apply | ||
ctx.Log.Debug(err.Error()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can verify the regex during the config processing stage?
Does a debug message make sense here? Or should it be an error :O
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can verify the regex during the config processing stage?
exactly regexp.Compile
I'll add specific comments to your code but in general here's the changes I think need to be made and why:
a.WebhooksManager.Send(webhookResult)
webhookManager, err := webhooks.NewWebhooksManager(webhooksConfig, config.SlackToken)
if err != nil {
return nil, errors.Wrap(err, "initializing webhooks")
}
type WebhooksSender interface {
Send(log *logging.SimpleLogger, result ApplyResult)
}
type WebhookSender interface {
Send(ApplyResult) error
} I've messed around with the code and created a pr back into your branch for what I'd like to see: nicholas-wu-hs#1. Feel free to just use that as a guide if you want to code it out yourself. |
server/server.go
Outdated
Channel string `mapstructure:"channel"` | ||
} | ||
|
||
type HookExecutorBuilder struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would all of this code out of the server
package and into events/webhooks
. Ask yourself if building a HookExecutor should be the responsibility of the server class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I thought about this too.
Previously, I tried to pass the the config structs from the server
package to the server/events/webhooks
code and ran into circular import problems, googled a bit for more info but found no clear solution.
In general, does a .go
file import packages from parent directories?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no it doesn't because of those circular import errors you ran into. You can either create a separate package (like models
) or you only reference from parent to child, never from child to parent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, thank you 👍
server/server.go
Outdated
Slack slack.Client | ||
} | ||
|
||
func (builder *HookExecutorBuilder) build(webhook Webhook) (events.HookExecutor, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pointer receivers should just be the first letter of the struct, in this case h *HookExecutorBuilder
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it now! Thanks, will do this for all future code!
server/server.go
Outdated
for _, webhook := range config.Webhooks { | ||
hookExecutor, err := hookExecutorBuilder.build(webhook) | ||
if err != nil { | ||
// TODO: check if it was because of uninitialized slack client |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my suggestions I have a WebhooksManager
that has a []WebhookSender
. When it gets constructed with no webhooks then it just has an empty array and so calling WebhooksManager.Send()
just does nothing. That's how I handled it.
When it's running through the webhooks in its constructor, it returns an error
if a slack webhook was specified but there was no slack-token
: https://github.com/nicholas-wu-hs/atlantis/pull/1/files?diff=unified#diff-0250d5b43682815402ad30ce1b068cdcR55
server/server.go
Outdated
@@ -86,6 +142,7 @@ func NewServer(config Config) (*Server, error) { | |||
} | |||
applyExecutor := &events.ApplyExecutor{ | |||
Github: githubClient, | |||
WSRegexToHook: applyExecutorRegexToHook, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here's where I would have liked to see something like a WebhooksManager
so the ApplyExecutor only needs to call a single line of code to send webhooks
server/server.go
Outdated
Webhooks []Webhook `mapstructure:"webhooks"` | ||
} | ||
|
||
type Webhook struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd call this WebhookConfig
since it's no an actual webhook, just config for it.
In my suggestions I don't pass this in directly to the WebhooksManager
. This is because I want the config that users need in their config files to be decoupled from the WebhooksManager. This will prevent us from accidentally changing the name of a config option in the WebhooksManager
code and invalidating everyone's config files :) We'll have to explicitly do the mapping: https://github.com/nicholas-wu-hs/atlantis/pull/1/files?diff=unified#diff-91bbeda7eb98a7adc57b9e47e2cf5c2bR72
server/events/apply_executor.go
Outdated
|
||
// execute apply webhooks | ||
for workspaceRegex, hookExecutor := range a.WSRegexToHook { | ||
matched, err := regexp.MatchString(workspaceRegex, ctx.Command.Environment) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you had constructed the regex during the construction of the slack webhook sender then here you could have a reference to *regexp.Regexp
and call .MatchString()
which won't return an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, thank you!
server/events/slack/slack_client.go
Outdated
client *slack.Client | ||
} | ||
|
||
type SlackMessageTemplate struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good call to have this as a struct rather than multiple params
server/events/slack/slack_client.go
Outdated
}, nil | ||
} | ||
|
||
func (s *ConcreteClient) PostMessage(channel string, template SlackMessageTemplate) (string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here you should have (c *ConcreteClient)
server/events/slack/slack_client.go
Outdated
return timestamp, err | ||
} | ||
|
||
func (s *ConcreteClient) createMessage(template SlackMessageTemplate) string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to do the attachment style message that you showed earlier. I think that looks best.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed that it looks nicer, one downside is that it takes a bit more room (1 attachment is at min ~3 lines), I'll play around a bit more with the message style.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah it takes up more room but I think it's worth it because it allows the information to be shown in a more consumable manner
server/events/apply_executor.go
Outdated
matched, err := regexp.MatchString(workspaceRegex, ctx.Command.Environment) | ||
if err != nil { | ||
// log the regexp error but let's continue with the apply | ||
ctx.Log.Debug(err.Error()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can verify the regex during the config processing stage?
exactly regexp.Compile
Still working on the tests, looking into using So this is another shot at using slack attachments, what do you think? |
server/events/apply_executor.go
Outdated
@@ -92,6 +94,16 @@ func (a *ApplyExecutor) apply(ctx *CommandContext, repoDir string, plan models.P | |||
env := ctx.Command.Environment | |||
tfApplyCmd := append(append(append([]string{"apply", "-no-color"}, applyExtraArgs...), ctx.Command.Flags...), plan.LocalPath) | |||
output, err := a.Terraform.RunCommandWithVersion(ctx.Log, absolutePath, tfApplyCmd, terraformVersion, env) | |||
|
|||
// Send webhooks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general comments should be used to
- explain why a piece of code is doing what it is
- explain what a large section of code is doing if it's not clear from just reading it
For 2. we should strive to write code that removes the need to comment it because it's so clear.
In this case, you've done that, it's pretty obvious what a.Webhooks.Send
is doing and so the comment Send webhooks.
doesn't tell me any more information.
The problem with comments is that they're a form of duplication and they're harder to keep up to date. If I rename Send()
to Queue()
then my code won't compile if I don't change the function everywhere, but your comment might get left behind and fall out of date.
tl;dr: remove this comment, it adds nothing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the insight, I agree with the view on what comments should be and developers should strive to write self-documenting code -- I'll will remove the comment :P
server/events/webhooks/slack.go
Outdated
return nil, errors.Wrap(err, "testing slack authentication") | ||
} | ||
|
||
// Make sure the slack channel exists. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good comment. I'd add
Make sure the slack channel exists so we can error early.
server/events/webhooks/slack.go
Outdated
func (s *SlackWebhook) createAttachments(result ApplyResult) []slack.Attachment { | ||
var color string | ||
if result.Success { | ||
color = "good" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make these constants at the top of file underneath the import
section:
const successColour = "good"
const failureColour = "danger"
(also color => colour because we're canadian :D 🇨🇦 )
server/events/webhooks/slack_test.go
Outdated
package webhooks_test | ||
|
||
// todo: actually test | ||
// purposefully empty to trigger coverage report |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice work figuring this out
configs := validConfigs() | ||
configs[0].WorkspaceRegex = invalidRegex | ||
_, err := webhooks.NewWebhooksManager(configs, validToken) | ||
Assert(t, strings.Contains(err.Error(), "error parsing regexp"), "expected err") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if err
is nil
then calling err.Error()
will cause a panic in the test
e.Error()
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x20 pc=0x1094fa6]
This is fine right now because your code is working but if someone breaks the code then the tests will be hard to figure out what's wrong because they will just have a panic
. So I'd add an extra test here
Assert(t, err != nil, "expected error")
And then if err
is not nil
you'll get an easier to debug failure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense, now I know and will nil
check errors in the tests now & for the future, ty!
configs := validConfigs() | ||
configs[0].Kind = unsupportedKind | ||
_, err := webhooks.NewWebhooksManager(configs, validToken) | ||
Assert(t, strings.Contains(err.Error(), "kind"), "expected err") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use Equals
and check the whole error string
Channel: validChannel, | ||
} | ||
|
||
func validConfigs() []webhooks.Config { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is this doing? Why are you appending an empty slice to validConfig
? Why not just have
var validConfig = []webhooks.Config{
{Event: ...}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I wanted to do was get a fresh copy of the valid configs for each test since I modify it in some tests.
So I think after having validConfig
as a variable per your example, I can do config := validConfig
in my tests to copy it.
Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left it as a function because I want new copies of the valid configs in each test, but I got rid of the appending.
func TestNewWebhooksManager_NoSlackToken(t *testing.T) { | ||
emptyToken := "" | ||
_, err := webhooks.NewWebhooksManager(validConfigs(), emptyToken) | ||
Assert(t, strings.Contains(err.Error(), "slack-token must be set"), "expected err") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should also test that if there are no configs then you don't need a slack-token
// todo: failing because not a valid token and channel | ||
m, err := webhooks.NewWebhooksManager(validConfigs(), validToken) | ||
Ok(t, err) | ||
Assert(t, m != nil, "mangager shouldn't be nil") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mangager => manager
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for the catch!
server/server.go
Outdated
} | ||
|
||
func NewServer(config Config) (*Server, error) { | ||
var webhooksConfig []webhooks.Config | ||
for _, c := range config.Webhooks { | ||
webhooksConfig = append(webhooksConfig, webhooks.Config{Channel: c.Channel, Event: c.Event, Kind: c.Kind, WorkspaceRegex: c.WorkspaceRegex}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's use a multiline config for the struct here since this line is getting long
197aa3f
to
4e57157
Compare
return ret0, ret1 | ||
} | ||
|
||
func (mock *MockSlackClient) PostMessage(channel string, result ApplyResult) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the generated code from pegomock
doesn't compile because it doesn't know what ApplyResult
is. (same for mock_webhooks.go
)
I tried adding in the package manually but then got errors about pegomock.GetGenericMockFrom(mock).Invoke
return type is incompatible.
Still figuring this out!
Still figuring out mocks and adding more tests to
I'll ask for a review after I'm done but wouldn't mind another review :) |
server/events/webhooks/slack.go
Outdated
failureColour = "danger" | ||
) | ||
|
||
//go:generate pegomock generate --use-experimental-model-gen --package mocks -o mocks/mock_slack.go slack.go |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change slack.go
to SlackClient
. Follow method 2. here: https://github.com/petergtz/pegomock#generating-mocks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, thank you ahahaha, that fixed the can't find ApplyResult
Now I'm looking into
mocks/mock_slack.go:50:9: no new variables on left side of :=
mocks/mock_slack.go:50:9: cannot use pegomock.GetGenericMockFrom(mock).Invoke("PostMessage", params, []reflect.Type literal) (type pegomock.ReturnValues) as type webhooks.ApplyResult in assignment
mocks/mock_slack.go:52:8: invalid argument result (type webhooks.ApplyResult) for len
mocks/mock_slack.go:53:12: invalid operation: result[0] (type webhooks.ApplyResult does not support indexing)
mocks/mock_slack.go:54:17: invalid operation: result[0] (type webhooks.ApplyResult does not support indexing)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as you mentioned, this was due to variable name clashing so I changed result
to applyResult
on the interface, thanks for catching this :)
server/events/webhooks/webhooks.go
Outdated
const SlackKind = "slack" | ||
const ApplyEvent = "apply" | ||
|
||
//go:generate pegomock generate --use-experimental-model-gen --package mocks -o mocks/mock_webhooks.go webhooks.go |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change webhooks.go
to WebhooksSender
|
Your commit isn't passing
You can run the |
I see! I think I know the problem from the error message, will add in the fixes. Should I add in the vendor dependencies (i.e. the slack library) as part of this pull request? |
server/events/webhooks/slack.go
Outdated
failureColour = "danger" | ||
) | ||
|
||
//go:generate pegomock generate --use-experimental-model-gen --package mocks -o mocks/mock_slack.go SlackClient |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use mocks/mocks_slack_client.go
to match the interface name rather than the name of this file. That way, if we generate a mock for more than one interface in this file, it won't delete the previously generated file (which would have the same name)
server/events/webhooks/slack.go
Outdated
PostMessage(channel string, applyResult ApplyResult) error | ||
} | ||
|
||
type ConcreteSlackClient struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've decided to go with Default
for the actual implementations of interfaces, so call this DefaultSlackClient
. Still need to fix a bunch of the other stuff but may as well start here.
server/events/webhooks/slack.go
Outdated
} | ||
} | ||
|
||
func NewSlack(r *regexp.Regexp, channel string, client SlackClient) (*SlackWebhook, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
split the slack client and webhook into two files slack_client.go
and slack.go
server/events/webhooks/slack.go
Outdated
|
||
type SlackClient interface { | ||
AuthTest() error | ||
ChannelExist(channelName string) (bool, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
call this ChannelExists
so it reads properly
RegisterMockTestingT(t) | ||
client := mocks.NewMockSlackClient() | ||
regex, err := regexp.Compile(".*") | ||
Ok(t, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice
server/events/webhooks/slack_test.go
Outdated
Environment: "production", | ||
} | ||
err = hook.Send(result) | ||
Ok(t, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
have you tried commenting out the line where you check if the regex matches (in the actual implementation) and seeing if this test passes (I think it will). You need to check that PostMessage
was never called. I think you can do something easy like client.VerifyWasCalled(Never()).PostMessage(...)
to ensure it is never called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, you're correct, the test passes commenting out the regex match.
client.VerifyWasCalled(Never()).PostMessage(...)
works and the test fails if i comment out the regex match! ty ty
Also renamed Concrete -> Default, Exist -> Exists
1cc684a
to
66a6f6f
Compare
1b67e1d
to
5d9ebaa
Compare
8cc6089
to
54e4207
Compare
Finished writing the tests |
Merged in #199! |
Tackling #179, first iteration using
slack-token
andslack-channel
in the config.If specified, a Slack Client will be created and attached to
ApplyExecutor
. It's used to post messages toslack-channel
on applies (both success and fail).Using https://github.com/nlopes/slack for slack client.
Will probably need a lot of refactoring/restructuring, then eventually tests and docs?
Also, was I supposed to add the dependencies into the repo?